package com.googlecode.lazyrecords.berkeleydb;

import com.googlecode.lazyrecords.Record;
import com.googlecode.lazyrecords.SourceRecord;
import com.googlecode.totallylazy.Closeables;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.Predicate;
import com.googlecode.totallylazy.iterators.StatefulIterator;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.ForwardCursor;
import com.sleepycat.je.LockMode;

import java.io.Closeable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.UUID;

import static com.sleepycat.je.OperationStatus.SUCCESS;

public final class CursorIterator extends StatefulIterator<Record> implements Closeable {
    private final Table table;
    private final Option<JeTransaction> transaction;
    private final Predicate<? super Record> predicate;
    private final DatabaseEntry key;
    private final DatabaseEntry value;
    private final RecordTuple tuple;
    private Cursor cursor;

    public CursorIterator(final Table table, final Option<JeTransaction> transaction, Predicate<? super Record> predicate) {
        this.table = table;
        this.transaction = transaction;
        this.predicate = predicate;
        key = new DatabaseEntry();
        value = new DatabaseEntry();
        tuple = new RecordTuple();
    }

    @Override
    protected Record getNext() throws Exception {
        if(!cursor().getNext(key, value, LockMode.DEFAULT).equals(SUCCESS)){
            close();
            return finished();
        }
        return SourceRecord.record(key(), tuple.entryToObject(value));
    }

    private UUID key() throws UnsupportedEncodingException {
        return UUID.fromString(new String(key.getData(), "UTF-8"));
    }

    private ForwardCursor cursor() {
        if(cursor == null){
            cursor = table.primary().openCursor(JeTransaction.transaction(transaction), new CursorConfig());
        }
        return cursor;
    }

    @Override
    public void close() throws IOException {
        Closeables.close(cursor);
    }
}
